package com.ruoyi.common.elasticsearch.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.ElasticsearchException;
import co.elastic.clients.elasticsearch._types.query_dsl.FuzzyQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
import co.elastic.clients.elasticsearch.indices.DeleteIndexRequest;
import co.elastic.clients.elasticsearch.indices.ExistsRequest;
import com.ruoyi.common.elasticsearch.model.EsPageResult;
import org.apache.http.HttpEntity;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.ruoyi.common.elasticsearch.service.ElasticSearchService;
import co.elastic.clients.elasticsearch.core.search.Hit;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Elasticsearch 通用服务实现类 (基于 Elasticsearch Java Client v8+)
 * 修复点：
 * 1. 移除 SearchRequest 泛型参数（8.x 客户端不支持请求构建时泛型）
 * 2. 统一泛型在 search() 方法调用时指定
 * 3. 修复异常类导入与抛出逻辑
 * 4. 清理重复导入与无效注释
 * 5. 补全模糊分页查询实现
 */
@Service
public class ElasticSearchServiceImpl implements ElasticSearchService {

    private static final Logger log = LoggerFactory.getLogger(ElasticSearchServiceImpl.class);
    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Resource
    private ElasticsearchClient esClient;

    @Resource
    private RestClient restClient; // Elasticsearch 低级 REST 客户端（用于自定义 HTTP 请求）

    /**
     * 保存单条数据
     * @param index 索引名
     * @param id    文档ID
     * @param data  数据对象
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    @Override
    public <T> void save(String index, String id, T data) {
        try {
            log.info("正在保存文档到索引 [{}], ID: [{}]", index, id);
            // IndexRequest 支持泛型（与 SearchRequest 不同，需保留）
            IndexRequest<T> request = new IndexRequest.Builder<T>()
                    .index(index)
                    .id(id)
                    .document(data)
                    .build();
            esClient.index(request);
            log.info("文档保存成功, 索引 [{}], ID: [{}]", index, id);
        } catch (Exception e) {
            log.error("保存文档失败, 索引 [{}], ID: [{}]", index, id, e);
            throw new RuntimeException();
        }
    }

    /**
     * 批量保存数据
     * @param index   索引名
     * @param dataMap 数据Map, key为ID, value为数据对象
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    @Override
    public <T> void bulkSave(String index, Map<String, T> dataMap) {
        if (dataMap == null || dataMap.isEmpty()) {
            log.warn("批量保存数据为空, 已忽略操作。");
            return;
        }
        try {
            log.info("正在批量保存 [{}] 条文档到索引 [{}]", dataMap.size(), index);
            List<BulkOperation> operations = dataMap.entrySet().stream()
                    .map(entry -> new BulkOperation.Builder()
                            .index(i -> i
                                    .index(index)
                                    .id(entry.getKey())
                                    .document(entry.getValue())
                            )
                            .build())
                    .collect(Collectors.toList());

            BulkRequest request = new BulkRequest.Builder()
                    .operations(operations)
                    .build();
            esClient.bulk(request);
            log.info("批量保存成功, 索引 [{}], 共 [{}] 条", index, dataMap.size());
        } catch (Exception e) {
            log.error("批量保存文档失败, 索引 [{}]", index, e);
            throw new RuntimeException();
        }
    }

    /**
     * 获取单条数据
     * @param index 索引名
     * @param id    文档ID
     * @param clazz 返回对象的Class
     * @return 文档对象, 如果不存在则返回null
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    @Override
    public <T> T get(String index, String id, Class<T> clazz) {
        try {
            log.info("正在获取文档, 索引 [{}], ID: [{}]", index, id);
            GetRequest request = new GetRequest.Builder()
                    .index(index)
                    .id(id)
                    .build();
            // GetResponse 泛型在 get() 方法调用时指定
            GetResponse<T> response = esClient.get(request, clazz);
            if (response.found()) {
                log.info("获取文档成功, 索引 [{}], ID: [{}]", index, id);
                return response.source();
            } else {
                log.warn("文档未找到, 索引 [{}], ID: [{}]", index, id);
                return null;
            }
        } catch (Exception e) {
            log.error("获取文档失败, 索引 [{}], ID: [{}]", index, id, e);
            throw new RuntimeException();
        }
    }

    /**
     * 单字段匹配查询 (无分页)
     * @param index  索引名
     * @param field  字段名
     * @param keyword 搜索关键词
     * @param clazz  返回对象的Class
     * @return 文档对象列表
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    @Override
    public <T> List<T> search(String index, String field, String keyword, Class<T> clazz) throws IOException {
        log.info("正在进行单字段搜索, 索引 [{}], 字段 [{}], 关键词 [{}]", index, field, keyword);
        // 修复：移除 SearchRequest 泛型参数
        SearchRequest request = new SearchRequest.Builder()
                .index(index)
                .query(q -> q
                        .match(m -> m
                                .field(field)
                                .query(keyword)
                        )
                )
                .build();
        return executeSearch(request, clazz);
    }

    /**
     * 单字段匹配查询 (带分页)
     * @param index  索引名
     * @param field  字段名
     * @param keyword 搜索关键词
     * @param page   页码 (从1开始)
     * @param size   每页大小
     * @param clazz  返回对象的Class
     * @return 文档对象列表
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    @Override
    public <T> List<T> search(String index, String field, String keyword, int page, int size, Class<T> clazz) {
        try {
            log.info("正在进行单字段搜索, 索引 [{}], 字段 [{}], 关键词 [{}], 页码 [{}], 每页 [{}]",
                    index, field, keyword, page, size);
            // 修复：移除 SearchRequest 泛型参数
            SearchRequest request = new SearchRequest.Builder()
                    .index(index)
                    .from((page - 1) * size) // 分页起始位置（从0开始）
                    .size(size) // 每页条数
                    .query(q -> q
                            .match(m -> m
                                    .field(field)
                                    .query(keyword)
                            )
                    )
                    .build();
            return executeSearch(request, clazz);
        } catch (Exception e) {
            log.error("单字段搜索失败", e);
            throw new RuntimeException();
        }
    }

    /**
     * 多字段匹配查询 (无分页，基于 HTTP 原生请求)
     * @param index  索引名
     * @param fields 字段列表
     * @param keyword 搜索关键词
     * @param clazz  返回对象的Class
     * @return 文档对象列表
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    @Override
    public <T> List<T> multiSearch(String index, List<String> fields, String keyword, Class<T> clazz) throws IOException {
        // 1. 构建 JSON 格式查询条件
        ObjectNode queryNode = objectMapper.createObjectNode();
        ObjectNode multiMatchNode = queryNode.putObject("query").putObject("multi_match");
        multiMatchNode.put("query", keyword);
        multiMatchNode.put("type", "best_fields"); // 最佳字段匹配策略

        // 添加多字段
        ArrayNode fieldsArray = multiMatchNode.putArray("fields");
        for (String field : fields) {
            fieldsArray.add(field);
        }
        String queryJson = queryNode.toString();
        log.info("多字段搜索 JSON: {}", queryJson);

        // 2. 发送 HTTP POST 请求
        Request request = new Request("POST", index + "/_search");
        request.setJsonEntity(queryJson);
        Response response = restClient.performRequest(request);

        // 3. 解析响应结果
        HttpEntity entity = response.getEntity();
        if (entity == null) {
            log.warn("多字段搜索响应为空, 索引 [{}]", index);
            return new ArrayList<>();
        }
        String responseJson = EntityUtils.toString(entity, "UTF-8");
        return parseSearchResponse(responseJson, clazz);
    }

    /**
     * 多字段匹配查询 (带分页，基于官方客户端)
     * @param index  索引名
     * @param fields 字段列表
     * @param keyword 搜索关键词
     * @param page   页码 (从1开始)
     * @param size   每页大小
     * @param clazz  返回对象的Class
     * @return 文档对象列表
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    @Override
    public <T> List<T> multiSearch(String index, List<String> fields, String keyword, int page, int size, Class<T> clazz) {
        try {
            log.info("正在进行多字段搜索, 索引 [{}], 字段 [{}], 关键词 [{}], 页码 [{}], 每页 [{}]",
                    index, fields, keyword, page, size);
            // 修复：移除 SearchRequest 泛型参数
            SearchRequest request = new SearchRequest.Builder()
                    .index(index)
                    .from((page - 1) * size)
                    .size(size)
                    .query(q -> q
                            .multiMatch(m -> m
                                    .query(keyword)
                                    .fields(fields)
                            )
                    )
                    .build();
            return executeSearch(request, clazz);
        } catch (Exception e) {
            log.error("多字段搜索失败", e);
            throw new RuntimeException();
        }
    }

    /**
     * 多字段模糊查询 (无分页)
     * @param index  索引名
     * @param fields 字段列表
     * @param keyword 搜索关键词
     * @param clazz  返回对象的Class
     * @return 文档对象列表
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    @Override
    public <T> List<T> fuzzyMultiSearch(String index, List<String> fields, String keyword, Class<T> clazz) throws IOException {
        log.info("正在进行多字段模糊搜索, 索引 [{}], 字段 [{}], 关键词 [{}]", index, fields, keyword);
        // 构建多个模糊查询（should 逻辑：满足任一条件）
        List<Query> shouldQueries = fields.stream()
                .map(field -> {
                    FuzzyQuery fuzzyQuery = new FuzzyQuery.Builder()
                            .field(field)
                            .value(keyword)
                            .fuzziness("AUTO") // 使用字符串"AUTO"替代Fuzziness.Auto
                            .build();
                    return new Query.Builder().fuzzy(fuzzyQuery).build();
                })
                .collect(Collectors.toList());

        // 修复：移除 SearchRequest 泛型参数
        SearchRequest request = new SearchRequest.Builder()
                .index(index)
                .query(q -> q
                        .bool(b -> b
                                .should(shouldQueries)
                                .minimumShouldMatch("1") // 至少匹配1个条件
                        )
                )
                .build();
        return executeSearch(request, clazz);
    }

//    @Override
//    public <T> List<T> fuzzyMultiSearch(String index, List<String> fields, String keyword, int page, int size, Class<T> clazz) {
//        return null;
//    }

    /**
     * 多字段模糊查询 (带分页)
     * @param index  索引名
     * @param fields 字段列表
     * @param keyword 搜索关键词
     * @param page   页码 (从1开始)
     * @param size   每页大小
     * @param clazz  返回对象的Class
     * @return 文档对象列表
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    // 在ElasticSearchServiceImpl中添加实现
    @Override
    public <T> EsPageResult<T> fuzzyMultiSearchWithTotal(String index, List<String> fields, String keyword,
                                                         int page, int size, Class<T> clazz) throws IOException {
        log.info("多字段模糊分页查询(带总数), 索引 [{}], 字段 [{}], 关键词 [{}], 页码 [{}], 每页 [{}]",
                index, fields, keyword, page, size);

        // 构建模糊查询条件（与原逻辑一致）
        List<Query> shouldQueries = fields.stream()
                .map(field -> new Query.Builder()
                        .fuzzy(f -> f
                                .field(field)
                                .value(keyword)
                                .fuzziness("AUTO")
                        )
                        .build()
                )
                .collect(Collectors.toList());

        // 构建搜索请求（包含分页）
        SearchRequest request = new SearchRequest.Builder()
                .index(index)
                .from((page - 1) * size)
                .size(size)
                .query(q -> q
                        .bool(b -> b
                                .should(shouldQueries)
                                .minimumShouldMatch("1")
                        )
                )
                .build();

        // 执行查询并获取响应（包含总数）
        SearchResponse<T> response = esClient.search(request, clazz);

        // 封装结果（数据列表 + 总记录数）
        EsPageResult<T> result = new EsPageResult<>();
        result.setRecords(response.hits().hits().stream()
                .map(hit -> hit.source())
                .collect(Collectors.toList()));
        // 从ES响应中提取总记录数（可能为null，需兜底）
        result.setTotal(response.hits().total() != null ? response.hits().total().value() : 0);

        return result;
    }
    /**
     * 多字段模糊分页查询 (独立实现)
     * @param index  索引名
     * @param fields 字段列表
     * @param keyword 搜索关键词
     * @param page   页码 (从1开始)
     * @param size   每页大小
     * @param clazz  返回对象的Class
     * @return 文档对象列表
     */
    @Override
    public <T> List<T> fuzzyMultiPageSearch(String index, List<String> fields, String keyword,
                                            int page, int size, Class<T> clazz) throws IOException {
        log.info("正在进行多字段模糊分页搜索, 索引 [{}], 字段 [{}], 关键词 [{}], 页码 [{}], 每页 [{}]",
                index, fields, keyword, page, size);
        // 构建模糊查询
        List<Query> shouldQueries = fields.stream()
                .map(field -> {
                    FuzzyQuery fuzzyQuery = new FuzzyQuery.Builder()
                            .field(field)
                            .value(keyword)
                            .fuzziness("AUTO") // 使用字符串"AUTO"替代Fuzziness.Auto
                            .build();
                    return new Query.Builder().fuzzy(fuzzyQuery).build();
                })
                .collect(Collectors.toList());

        // 构建搜索请求
        SearchRequest request = new SearchRequest.Builder()
                .index(index)
                .from((page - 1) * size)
                .size(size)
                .query(q -> q
                        .bool(b -> b
                                .should(shouldQueries)
                                .minimumShouldMatch("1")
                        )
                )
                .build();

        // 执行搜索并返回结果（调用executeSearch方法）
        return executeSearch(request, clazz);
    }


    /**
     * 删除单条数据
     * @param index 索引名
     * @param id    文档ID
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    @Override
    public void delete(String index, String id) {
        try {
            log.info("正在删除文档, 索引 [{}], ID: [{}]", index, id);
            DeleteRequest request = new DeleteRequest.Builder()
                    .index(index)
                    .id(id)
                    .build();
            esClient.delete(request);
            log.info("文档删除成功, 索引 [{}], ID: [{}]", index, id);
        } catch (Exception e) {
            log.error("删除文档失败, 索引 [{}], ID: [{}]", index, id, e);
            throw new RuntimeException();
        }
    }

    /**
     * 判断索引是否存在
     * @param index 索引名
     * @return true 存在, false 不存在
     */
    @Override
    public boolean existsIndex(String index) {
        try {
            ExistsRequest request = new ExistsRequest.Builder().index(index).build();
            return esClient.indices().exists(request).value();
        } catch (Exception e) {
            log.error("判断索引是否存在失败, 索引 [{}]", index, e);
            throw new RuntimeException();
        }
    }

    /**
     * 删除索引 (不可逆操作)
     * @param index 索引名
     */
    @Override
    public void deleteIndex(String index) {
        try {
            if (!existsIndex(index)) {
                log.warn("索引 [{}] 不存在, 无需删除", index);
                return;
            }
            log.warn("正在删除索引 [{}], 此操作不可逆!", index);
            DeleteIndexRequest request = new DeleteIndexRequest.Builder().index(index).build();
            esClient.indices().delete(request);
            log.info("索引 [{}] 删除成功", index);
        } catch (Exception e) {
            log.error("删除索引失败, 索引 [{}]", index, e);
        }
    }

    /**
     * 单字段精确匹配查询 (无分页)
     * @param index  索引名
     * @param field  字段名
     * @param value  精确匹配值
     * @param clazz  返回对象的Class
     * @return 文档对象列表
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    @Override
    public <T> List<T> termSearch(String index, String field, Object value, Class<T> clazz) throws IOException {
        log.info("单字段精确查询, 索引 [{}], 字段 [{}], 值 [{}]", index, field, value);

        // 构建 term 查询
        Query query = new Query.Builder()
                .term(t -> t
                        .field(field)
                        .value(v -> v.stringValue(value.toString()))
                )
                .build();

        // 构建搜索请求
        co.elastic.clients.elasticsearch.core.SearchRequest request = new co.elastic.clients.elasticsearch.core.SearchRequest.Builder()
                .index(index)
                .query(query)
                .build();

        return executeSearch(request, clazz);
    }
    /**
     * 单字段精确匹配查询 (带分页)
     * @param index  索引名
     * @param field  字段名
     * @param value  精确匹配值
     * @param page   页码 (从1开始)
     * @param size   每页大小
     * @param clazz  返回对象的Class
     * @return 文档对象列表
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    /**
     * 单字段精确匹配分页查询（带总数）
     */
    @Override
    public <T> EsPageResult<T> termSearchWithTotal(String index, String field, Object value,
                                                   int page, int size, Class<T> clazz) throws IOException {
        log.info("单字段精确分页查询(带总数), 索引 [{}], 字段 [{}], 值 [{}], 页码 [{}], 每页 [{}]",
                index, field, value, page, size);

        // 构建term查询
        Query query = new Query.Builder()
                .term(t -> t
                        .field(field)
                        .value(v -> v.stringValue(value.toString()))
                )
                .build();

        // 构建搜索请求
        SearchRequest request = new SearchRequest.Builder()
                .index(index)
                .from((page - 1) * size)
                .size(size)
                .query(query)
                .build();

        // 执行查询
        SearchResponse<T> response = esClient.search(request, clazz);

        // 封装结果
        EsPageResult<T> result = new EsPageResult<>();
        result.setRecords(response.hits().hits().stream()
                .map(Hit::source)
                .collect(Collectors.toList()));
        result.setTotal(response.hits().total() != null ? response.hits().total().value() : 0);

        return result;
    }
    /**
     * 多字段精确匹配查询 (带分页)
     * @param index   索引名
     * @param termMap 字段-值映射 (精确匹配)
     * @param page    页码 (从1开始)
     * @param size    每页大小
     * @param clazz   返回对象的Class
     * @return 文档对象列表
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    @Override
    public <T> List<T> termSearch(String index, Map<String, Object> termMap, int page, int size, Class<T> clazz) throws IOException {
        if (termMap == null || termMap.isEmpty()) {
            log.warn("多字段精确查询条件为空, 返回空列表");
            return new ArrayList<>();
        }

        log.info("多字段精确分页查询, 索引 [{}], 条件 [{}], 页码 [{}], 每页 [{}]",
                index, termMap, page, size);

        // 构建 bool 查询，must 条件（所有字段必须匹配）
        List<Query> mustQueries = termMap.entrySet().stream()
                .map(entry -> new Query.Builder()
                        .term(t -> t
                                .field(entry.getKey())
                                .value(v -> v.stringValue(entry.getValue().toString()))
                        )
                        .build())
                .collect(Collectors.toList());

        // 构建搜索请求
        co.elastic.clients.elasticsearch.core.SearchRequest request = new co.elastic.clients.elasticsearch.core.SearchRequest.Builder()
                .index(index)
                .from((page - 1) * size)
                .size(size)
                .query(q -> q
                        .bool(b -> b
                                .must(mustQueries)
                        )
                )
                .build();

        return executeSearch(request, clazz);
    }
    /**
     * 多字段精确匹配查询 (带分页和总数)
     * @param index   索引名
     * @param termMap 字段-值映射 (精确匹配)
     * @param page    页码 (从1开始)
     * @param size    每页大小
     * @param clazz   返回对象的Class
     * @return 包含分页信息和总数的结果
     */
    @Retryable(
            value = {IOException.class, ElasticsearchException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    @Override
    public <T> EsPageResult<T> termSearchWithTotal(String index, Map<String, Object> termMap,
                                                   int page, int size, Class<T> clazz) throws IOException {
        if (termMap == null || termMap.isEmpty()) {
            log.warn("多字段精确查询条件为空, 返回空结果");
            return new EsPageResult<>();
        }

        log.info("多字段精确分页查询(带总数), 索引 [{}], 条件 [{}], 页码 [{}], 每页 [{}]",
                index, termMap, page, size);

        // 构建bool must查询（所有字段必须匹配）
        List<Query> mustQueries = termMap.entrySet().stream()
                .map(entry -> new Query.Builder()
                        .term(t -> t
                                .field(entry.getKey())
                                .value(v -> v.stringValue(entry.getValue().toString()))
                        )
                        .build())
                .collect(Collectors.toList());

        // 构建搜索请求
        SearchRequest request = new SearchRequest.Builder()
                .index(index)
                .from((page - 1) * size)
                .size(size)
                .query(q -> q
                        .bool(b -> b
                                .must(mustQueries)
                        )
                )
                .build();

        // 执行查询
        SearchResponse<T> response = esClient.search(request, clazz);

        // 封装结果
        EsPageResult<T> result = new EsPageResult<>();
        result.setRecords(response.hits().hits().stream()
                .map(Hit::source)
                .collect(Collectors.toList()));
        result.setTotal(response.hits().total() != null ? response.hits().total().value() : 0);

        return result;
    }
    /**
     * 执行搜索并解析响应 (通用辅助方法)
     * @param request 搜索请求（无泛型）
     * @param clazz   返回对象的Class
     * @return 文档对象列表
     */
    private <T> List<T> executeSearch(SearchRequest request, Class<T> clazz) throws IOException {
        // 修复：泛型在 search() 方法调用时通过 clazz 参数指定
        SearchResponse<T> response = esClient.search(request, clazz);
        long totalHits = response.hits().total() != null ? response.hits().total().value() : 0;
        log.info("搜索完成, 索引 [{}], 命中总数: [{}]", request.index(), totalHits);

        // 解析命中结果
        return response.hits().hits().stream()
                .map(hit -> hit.source()) // 提取文档源数据
                .collect(Collectors.toList());
    }

    /**
     * 解析 JSON 格式的搜索响应 (辅助方法，用于原生 HTTP 请求)
     * @param responseJson 响应 JSON 字符串
     * @param clazz        返回对象的Class
     * @return 文档对象列表
     */
    private <T> List<T> parseSearchResponse(String responseJson, Class<T> clazz) throws IOException {
        com.fasterxml.jackson.databind.JsonNode rootNode = objectMapper.readTree(responseJson);
        com.fasterxml.jackson.databind.JsonNode hitsNode = rootNode.get("hits").get("hits");

        List<T> results = new ArrayList<>();
        if (hitsNode == null || !hitsNode.isArray()) {
            log.warn("搜索响应格式异常, 无命中数据");
            return results;
        }

        // 遍历命中的文档
        for (com.fasterxml.jackson.databind.JsonNode hit : hitsNode) {
            com.fasterxml.jackson.databind.JsonNode sourceNode = hit.get("_source");
            if (sourceNode != null) {
                T result = objectMapper.treeToValue(sourceNode, clazz);
                results.add(result);
            }
        }
        log.info("解析搜索响应完成, 共获取 [{}] 条数据", results.size());
        return results;
    }






    /**
     * 嵌套查询实现 (针对单个字段)
     */
    @Override
    public <T> EsPageResult<T> nestedSearch(String index, String path, String field,
                                            String keyword, int page, int size, Class<T> clazz) throws IOException {
        log.info("嵌套字段搜索, 索引 [{}], 嵌套路径 [{}], 字段 [{}], 关键词 [{}], 页码 [{}], 每页 [{}]",
                index, path, field, keyword, page, size);

        // 构建嵌套查询中的匹配查询
        Query nestedMatchQuery = new Query.Builder()
                .match(m -> m
                        .field(field)
                        .query(keyword)
                )
                .build();

        // 构建嵌套查询
        Query query = new Query.Builder()
                .nested(n -> n
                        .path(path)
                        .query(nestedMatchQuery)
                )
                .build();

        return executeNestedSearch(index, query, page, size, clazz);
    }

    /**
     * 复杂嵌套查询实现 (支持自定义嵌套查询条件)
     */
    @Override
    public <T> EsPageResult<T> nestedSearch(String index, String path, Query nestedQuery,
                                            int page, int size, Class<T> clazz) throws IOException {
        log.info("复杂嵌套字段搜索, 索引 [{}], 嵌套路径 [{}], 页码 [{}], 每页 [{}]",
                index, path, page, size);

        // 构建嵌套查询
        Query query = new Query.Builder()
                .nested(n -> n
                        .path(path)
                        .query(nestedQuery)
                )
                .build();

        return executeNestedSearch(index, query, page, size, clazz);
    }

    /**
     * 执行嵌套搜索的通用方法
     */
    private <T> EsPageResult<T> executeNestedSearch(String index, Query query,
                                                    int page, int size, Class<T> clazz) throws IOException {
        // 构建搜索请求
        SearchRequest request = new SearchRequest.Builder()
                .index(index)
                .from((page - 1) * size)
                .size(size)
                .query(query)
                .build();

        // 执行查询
        SearchResponse<T> response = esClient.search(request, clazz);

        // 封装结果
        EsPageResult<T> result = new EsPageResult<>();
        result.setRecords(response.hits().hits().stream()
                .map(Hit::source)
                .collect(Collectors.toList()));
        result.setTotal(response.hits().total() != null ? response.hits().total().value() : 0);

        return result;
    }

    /**
     * 嵌套字段精确查询
     */
    @Override
    public <T> EsPageResult<T> nestedTermSearch(String index, String path, String field,
                                                Object value, int page, int size, Class<T> clazz) throws IOException {
        log.info("嵌套字段精确查询, 索引 [{}], 嵌套路径 [{}], 字段 [{}], 值 [{}], 页码 [{}], 每页 [{}]",
                index, path, field, value, page, size);

        // 构建嵌套查询中的term查询
        Query nestedTermQuery = new Query.Builder()
                .term(t -> t
                        .field(field)
                        .value(v -> v.stringValue(value.toString()))
                )
                .build();

        // 构建嵌套查询
        Query query = new Query.Builder()
                .nested(n -> n
                        .path(path)
                        .query(nestedTermQuery)
                )
                .build();

        return executeNestedSearch(index, query, page, size, clazz);
    }

    /**
     * 嵌套字段模糊查询
     */
    @Override
    public <T> EsPageResult<T> nestedFuzzySearch(String index, String path, String field,
                                                 String keyword, int page, int size, Class<T> clazz) throws IOException {
        log.info("嵌套字段模糊查询, 索引 [{}], 嵌套路径 [{}], 字段 [{}], 关键词 [{}], 页码 [{}], 每页 [{}]",
                index, path, field, keyword, page, size);

        // 构建嵌套查询中的模糊查询
        Query nestedFuzzyQuery = new Query.Builder()
                .fuzzy(f -> f
                        .field(field)
                        .value(keyword)
                        .fuzziness("AUTO")
                )
                .build();

        // 构建嵌套查询
        Query query = new Query.Builder()
                .nested(n -> n
                        .path(path)
                        .query(nestedFuzzyQuery)
                )
                .build();

        return executeNestedSearch(index, query, page, size, clazz);
    }
}